
//--Yangxin--

`include "defines.v"

module SimTop(
    input         clock,
    input         reset,

    input  [63:0] io_logCtrl_log_begin,
    input  [63:0] io_logCtrl_log_end,
    input  [63:0] io_logCtrl_log_level,
    input         io_perfInfo_clean,
    input         io_perfInfo_dump,

    output        io_uart_out_valid,
    output [7:0]  io_uart_out_ch,
    output        io_uart_in_valid,
    input  [7:0]  io_uart_in_ch
);

localparam CSR_MCYCLE = 12'hb00;
// if_stage
wire [63 : 0] pc;
wire [31 : 0] inst;


wire rd_w_ena;
wire [4 : 0]rd_w_addr;

// regfile -> id_stage

// regfile -> difftest
wire [`REG_BUS] regs[0 : 31];

// exe_stage
// exe_stage -> other stage
// exe_stage -> regfile
wire [`REG_BUS]rd_data;


wire        clk;
assign clk = clock;

wire        inst_sram_en;
wire [7:0]  inst_sram_wen;
wire [63:0] inst_sram_addr;
wire [63:0] inst_sram_wdata;
wire [31:0] inst_sram_rdata;
wire        data_sram_wr_rd;

assign pc   = debug_wb_pc  ;
assign inst = debug_wb_inst;


wire                         data_sram_en   ;
wire [                  7:0] data_sram_wen  ;
wire [                 63:0] data_sram_addr ;
wire [                 63:0] data_sram_wdata;
wire [				         63:0] data_sram_mask ;
wire [                 63:0] data_sram_rdata;


wire                         ds_allowin    ;
wire                         es_allowin    ;
wire                         ms_allowin    ;
wire                         ws_allowin    ;
wire                         fs_to_ds_valid;
wire                         ds_to_es_valid;
wire                         es_to_ms_valid;
wire                         ms_to_ws_valid;
wire [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus  ;
wire [`DS_TO_ES_BUS_WD -1:0] ds_to_es_bus  ;
wire [`ES_TO_MS_BUS_WD -1:0] es_to_ms_bus  ;
wire [`MS_TO_WS_BUS_WD -1:0] ms_to_ws_bus  ;
wire [`WS_TO_RF_BUS_WD -1:0] ws_to_rf_bus  ;
wire [`BR_BUS_WD       -1:0] br_bus        ;
wire [				         69:0] es_to_ds_fwd  ;
wire [                 68:0] ms_to_ds_fwd  ;
wire [                 68:0] ws_to_ds_fwd  ;

//csr
wire [                128:0] csr_ms_to_es_fwd;
wire [				        128:0] csr_ws_to_es_fwd;

//to csr_regs
wire [				         63:0] csr_regs_raddr  ;
wire [				         63:0] csr_regs_rdata  ;

wire [`WS_TO_CSR_BUS_WD-1:0] ws_to_csr_bus   ;



wire [63:0] regs[0:31];

    //trace debug inteface: I think it is similar to difftest
wire [                 63:0] debug_wb_pc      ;
wire [                 31:0] debug_wb_inst    ; 
wire 						             debug_wb_rf_we   ;
wire [                  4:0] debug_wb_rf_waddr;
wire [                 63:0] debug_wb_rf_wdata;  
wire                         debug_wb_valid   ;

//except
wire                         except_flush     ;
wire [				         63:0] except_new_pc    ;

wire [                 63:0] csr_mtvec_to_ws  ;
wire [                 63:0] csr_mepc_to_ws   ;
wire [                 63:0] csr_mstatus_to_ws;
wire                         csr_mie_mtie_to_ws;
wire [`WS_EXCEPT_BUS_WD-1:0] ws_except_bus    ;

//timer interrupt
wire                         timer_interrupt  ;
wire [                 63:0] ms_pc_to_ws      ;
wire                         ms_valid_to_ws   ;
wire [                 63:0] es_pc_to_ws      ;
wire                         es_valid_to_ws   ; 
wire                         true_timer_interrupt;



reg                          true_timer_interrupt_d1;//
// assign pc   = debug_wb_pc  ;
// assign inst = debug_wb_inst;

reg [63:0] pc_d1;
reg [31:0] inst_d1;
always @(posedge clk) begin
  true_timer_interrupt_d1 <= true_timer_interrupt;
  pc_d1  <= pc;
  inst_d1 <= inst;
end



//IF stage
if_stage inst_if_stage
    (
	.clk             (clk           ),
	.reset           (reset         ),
	//allowin
	.ds_allowin      (ds_allowin    ),
	//brbus
	.br_bus          (br_bus        ),
	//outputs
	.fs_to_ds_valid  (fs_to_ds_valid),
	.fs_to_ds_bus    (fs_to_ds_bus  ),
	//inst sram interfece
	//.inst_addr       (inst_addr),
	.inst_sram_en    (inst_sram_en   ),
	.inst_sram_wen   (inst_sram_wen  ),
	.inst_sram_addr  (inst_sram_addr ),
	.inst_sram_wdata (inst_sram_wdata),
	.inst_sram_rdata (inst_sram_rdata),
  .except_flush    (except_flush   ),
  .except_new_pc   (except_new_pc  )
    );

//ID stage
id_stage inst_id_stage
	(
		.clk            (clk           ),
		.reset          (reset         ),
		//allowin
		.es_allowin     (es_allowin    ),
		.ds_allowin     (ds_allowin    ),
		//from fs
		.fs_to_ds_valid (fs_to_ds_valid),
		.fs_to_ds_bus   (fs_to_ds_bus  ),
		// to es
		.ds_to_es_valid (ds_to_es_valid),
		.ds_to_es_bus   (ds_to_es_bus  ),
		//to fs
		.br_bus         (br_bus        ),
		//forward
		.es_to_ds_fwd   (es_to_ds_fwd  ),
		.ms_to_ds_fwd   (ms_to_ds_fwd  ),
		.ws_to_ds_fwd   (ws_to_ds_fwd  ),
		//to rf:for write back
		.ws_to_rf_bus   (ws_to_rf_bus  ),
    .regs_o         (regs          ),     //difftest 
    .except_flush   (except_flush  )
	);

//EXE_stage
exe_stage inst_exe_stage
	(
		.clk             (clk             ),
		.reset           (reset           ),
		//allowin
		.ms_allowin      (ms_allowin      ),
		.es_allowin      (es_allowin      ),
		//from ds
		.ds_to_es_valid  (ds_to_es_valid  ),
		.ds_to_es_bus    (ds_to_es_bus    ),
		//to ms
		.es_to_ms_valid  (es_to_ms_valid  ),
		.es_to_ms_bus    (es_to_ms_bus    ),
		//forward
		.es_to_ds_fwd    (es_to_ds_fwd    ),
    //csr forward
	  .csr_ms_to_es_fwd(csr_ms_to_es_fwd),
    .csr_ws_to_es_fwd(csr_ws_to_es_fwd),
		//data sram interface
		.data_sram_en    (data_sram_en    ),
		.data_sram_wen   (data_sram_wen   ),
		.data_sram_addr  (data_sram_addr  ),
    .data_sram_mask  (data_sram_mask  ),
		.data_sram_wdata (data_sram_wdata ),
    .data_sram_wr_rd (data_sram_wr_rd ),
    //csr_regs
    .csr_regs_raddr  (csr_regs_raddr  ),
	  .csr_regs_rdata  (csr_regs_rdata  ),
    //except
    .except_flush    (except_flush    ),
    .es_pc_to_ws     (es_pc_to_ws     ),
    .es_valid_to_ws  (es_valid_to_ws  )
	);

wire [63:0] mtimecmp_o; 
wire [63:0] mtime_o; 



//MEM_stage
mem_stage inst_mem_stage
	(
		.clk                (clk             ),
		.reset              (reset           ),
		//allowin
		.ws_allowin         (ws_allowin      ),
		.ms_allowin         (ms_allowin      ),
		//from es
		.es_to_ms_valid     (es_to_ms_valid  ),
		.es_to_ms_bus       (es_to_ms_bus    ),
		//to ws
		.ms_to_ws_valid     (ms_to_ws_valid  ),
		.ms_to_ws_bus       (ms_to_ws_bus    ),
		//forward
		.ms_to_ds_fwd       (ms_to_ds_fwd    ),
    .csr_ms_to_es_fwd   (csr_ms_to_es_fwd),
		//from data-sram
		.data_sram_rdata_d0 (data_sram_rdata ),
    //except
    .except_flush       (except_flush    ),
    .ms_pc_to_ws        (ms_pc_to_ws     ),
    .ms_valid_to_ws     (ms_valid_to_ws  ),
    .mtimecmp_i         (mtimecmp_o      ),
    .mtime_i            (mtime_o         )         
	);


//WB_stage
wb_stage inst_wb_stage
	(
		.clk              (clk              ),
		.reset            (reset            ),
		//allowin
		.ws_allowin       (ws_allowin       ),
		//from ms
		.ms_to_ws_valid   (ms_to_ws_valid   ),
		.ms_to_ws_bus     (ms_to_ws_bus     ),
		//to rf
		.ws_to_rf_bus     (ws_to_rf_bus     ),
    //to csr
	  .ws_to_csr_bus    (ws_to_csr_bus    ), 
    .ws_except_bus    (ws_except_bus    ),
		//forward
		.ws_to_ds_fwd     (ws_to_ds_fwd     ),
    .csr_ws_to_es_fwd (csr_ws_to_es_fwd ), 
    //csr_regs_forward
    .csr_mtvec        (csr_mtvec_to_ws  ),
    .csr_mepc         (csr_mepc_to_ws   ),
    .csr_mstatus      (csr_mstatus_to_ws),
    .csr_mie_mtie     (csr_mie_mtie_to_ws),

    .debug_wb_pc      (debug_wb_pc      ),
    .debug_wb_inst    (debug_wb_inst    ),
    .debug_wb_rf_we   (debug_wb_rf_we   ),
    .debug_wb_rf_waddr(debug_wb_rf_waddr),
    .debug_wb_rf_wdata(debug_wb_rf_wdata),
    .debug_wb_valid   (debug_wb_valid   ),
    //except
    .except_flush     (except_flush     ),
    .except_new_pc    (except_new_pc    ),
    //timer interrupt
    .timer_interrupt  (timer_interrupt  ),
    .true_timer_interrupt(true_timer_interrupt),
    .ms_pc_to_ws      (ms_pc_to_ws      ),
    .ms_valid_to_ws   (ms_valid_to_ws   ),
    .es_pc_to_ws      (es_pc_to_ws      ),
    .es_valid_to_ws   (es_valid_to_ws   ),
    .ws_skip          (ws_skip          )
	);




clint u_clint(
	//ports
	.clk             		( clk             		),
	.reset           		( reset           		),
	.addr            		( data_sram_addr      ),
	.wdata           		( data_sram_wdata     ),
	.wr_en           		( data_sram_wen[0]    ),
  .mtimecmp_o         ( mtimecmp_o          ),
  .mtime_o            ( mtime_o             ),
	.timer_interrupt 		( timer_interrupt 		)
);



//csr_regs
wire        	global_int_en_o;
wire [63:0] 	csr_mcycle_o   ;
wire [63:0] 	csr_mtvec_o    ;
wire [63:0] 	csr_mcause_o   ;
wire [63:0] 	csr_mepc_o     ;
wire [63:0] 	csr_mstatus_o  ;
wire [63:0] 	csr_mie_o      ;
wire [63:0]   csr_mip_o      ;
wire [63:0]   csr_mscratch_o ;
wire [63:0]   csr_sstatus_o  ;



csr_reg u_csr_reg(
	//ports
	.clk             		( clk             		 ),
	.reset           		( reset           		 ),
  //from wb
	.we              		( ws_to_csr_bus[128]   ),
	.raddr           		( csr_regs_raddr       ),
	.waddr           		( ws_to_csr_bus[127:64]),
	.wdata           		( ws_to_csr_bus[  63:0]),
	.rdata           		( csr_regs_rdata       ),
	.global_int_en_o 		( global_int_en_o 		 ),
  //ecall & mret
  .csr_except_ctrl    ( ws_except_bus        ),
  //difftest
	.csr_mcycle_o    		( csr_mcycle_o    		 ),
	.csr_mtvec_o     		( csr_mtvec_o     		 ),
	.csr_mcause_o    		( csr_mcause_o    		 ),
	.csr_mepc_o      		( csr_mepc_o      		 ),
	.csr_mstatus_o   		( csr_mstatus_o   		 ),
	.csr_mie_o       		( csr_mie_o       		 ),
  .csr_mip_o          ( csr_mip_o            ),
  .csr_mscratch_o     ( csr_mscratch_o       ),
  //csr_regs
  .csr_mtvec_to_ws    ( csr_mtvec_to_ws      ),
  .csr_mepc_to_ws     ( csr_mepc_to_ws       ),
  .csr_mstatus_to_ws  ( csr_mstatus_to_ws    ),
  .csr_mie_mtie_to_ws ( csr_mie_mtie_to_ws   ),
  .csr_sstatus_o      ( csr_sstatus_o        ) 
);





//Access data memory
// wire [63:0] pc;
// assign pc = fs_pc;
// reg [63:0] rdata;
RAMHelper RAMHelper(
  .clk              (clk),
  .en               (data_sram_en),
  .rIdx             ((data_sram_addr - `PC_START) >> 3),
  .rdata            (data_sram_rdata),
  .wIdx             ((data_sram_addr - `PC_START) >> 3),
  .wdata            (data_sram_wdata),
  .wmask            (data_sram_mask),
  .wen              (data_sram_wr_rd)
);
// wire [31:0] inst;
// assign inst = pc[2] ? rdata[63 : 32] : rdata[31 : 0];

// assign fs_inst = inst;




// Difftest
reg cmt_wen;
reg [7:0] cmt_wdest;
reg [`REG_BUS] cmt_wdata;
reg [`REG_BUS] cmt_pc;
reg [31:0] cmt_inst;
reg cmt_valid;
reg trap;
reg [7:0] trap_code;
reg [63:0] cycleCnt;
reg [63:0] instrCnt;
reg [`REG_BUS] regs_diff [0 : 31];

//interrupt difftest
reg [`REG_BUS] exceptionPC;
reg [31:0]     exceptionInst; 
reg [31:0]     intrNO;    //7 or 11
reg [31:0]     cause  ;




//csr difftest
reg [63:0] csr_mcycle_diff ;
reg [63:0] csr_mtvec_diff  ;
reg [63:0] csr_mcause_diff ;
reg [63:0] csr_mepc_diff   ;
reg [63:0] csr_mstatus_diff;
reg [63:0] csr_mie_diff    ;
reg [63:0] csr_mip_diff    ;
reg [63:0] csr_mscratch_diff    ;
reg [63:0] csr_sstatus_diff;

assign rd_w_ena  = debug_wb_rf_we           ;
assign rd_w_addr = {3'd0, debug_wb_rf_waddr};
assign rd_data   = debug_wb_rf_wdata        ;

wire ws_skip;
//wire inst_valid = (pc != `PC_START) | (inst != 0);
wire inst_valid = (((pc != `PC_START) | (inst != 0)) && debug_wb_valid && (~true_timer_interrupt));

wire wb_skip = (inst == 32'h7b || (ws_skip) ||(ws_to_csr_bus[75:64] == CSR_MCYCLE && ws_to_csr_bus[128])) ? 1'b1: 1'b0;
 //output        io_uart_out_valid,
   // output [7:0]  io_uart_out_ch,
assign io_uart_out_valid = (cmt_inst==64'h7b) ? 1'b1 : 1'b0;
assign io_uart_out_ch = (cmt_inst==64'h7b) ? regs[10][7:0] : 8'b0;

always @(negedge clock) begin
  if (reset) begin
    {cmt_wen, cmt_wdest, cmt_wdata, cmt_pc, cmt_inst, cmt_valid, trap, trap_code, cycleCnt, instrCnt} <= 0;
  end
  else if (~trap) begin
    cmt_wen <= rd_w_ena;
    cmt_wdest <= {3'd0, rd_w_addr};
    cmt_wdata <= rd_data;
    cmt_pc <= pc;
    cmt_inst <= inst;
    cmt_valid <= inst_valid;

		regs_diff <= regs;
    //csr addr
    csr_mcycle_diff  <= csr_mcycle_o ;
    csr_mtvec_diff   <= csr_mtvec_o  ;
    csr_mcause_diff  <= csr_mcause_o ;
    csr_mepc_diff    <= csr_mepc_o   ;
    csr_mstatus_diff <= csr_mstatus_o;
    csr_mie_diff     <= csr_mie_o    ;
    csr_mip_diff     <= csr_mip_o    ;
    csr_mscratch_diff<= csr_mscratch_o;
    csr_sstatus_diff <= csr_sstatus_o;

    //interrupt
    exceptionPC      <= pc & {64{true_timer_interrupt}};
    exceptionInst   <= inst & {32{true_timer_interrupt}};
    intrNO          <= {32{true_timer_interrupt}} & {32'd7};
    cause            <= {32{true_timer_interrupt}} & {32'd7};


    trap <= inst[6:0] == 7'h6b;
    trap_code <= regs[10][7:0];
    cycleCnt <= cycleCnt + 1;
    instrCnt <= instrCnt + inst_valid;
  end
end

DifftestInstrCommit DifftestInstrCommit(
  .clock              (clock),
  .coreid             (0),
  .index              (0),
  .valid              (cmt_valid),
  .pc                 (cmt_pc),
  .instr              (cmt_inst),
  .special            (0),
  .skip               (wb_skip),
  .isRVC              (0),
  .scFailed           (0),
  .wen                (cmt_wen),
  .wdest              (cmt_wdest),
  .wdata              (cmt_wdata)
);

DifftestArchIntRegState DifftestArchIntRegState (
  .clock              (clock),
  .coreid             (0),
  .gpr_0              (regs_diff[0]),
  .gpr_1              (regs_diff[1]),
  .gpr_2              (regs_diff[2]),
  .gpr_3              (regs_diff[3]),
  .gpr_4              (regs_diff[4]),
  .gpr_5              (regs_diff[5]),
  .gpr_6              (regs_diff[6]),
  .gpr_7              (regs_diff[7]),
  .gpr_8              (regs_diff[8]),
  .gpr_9              (regs_diff[9]),
  .gpr_10             (regs_diff[10]),
  .gpr_11             (regs_diff[11]),
  .gpr_12             (regs_diff[12]),
  .gpr_13             (regs_diff[13]),
  .gpr_14             (regs_diff[14]),
  .gpr_15             (regs_diff[15]),
  .gpr_16             (regs_diff[16]),
  .gpr_17             (regs_diff[17]),
  .gpr_18             (regs_diff[18]),
  .gpr_19             (regs_diff[19]),
  .gpr_20             (regs_diff[20]),
  .gpr_21             (regs_diff[21]),
  .gpr_22             (regs_diff[22]),
  .gpr_23             (regs_diff[23]),
  .gpr_24             (regs_diff[24]),
  .gpr_25             (regs_diff[25]),
  .gpr_26             (regs_diff[26]),
  .gpr_27             (regs_diff[27]),
  .gpr_28             (regs_diff[28]),
  .gpr_29             (regs_diff[29]),
  .gpr_30             (regs_diff[30]),
  .gpr_31             (regs_diff[31])
);

DifftestTrapEvent DifftestTrapEvent(
  .clock              (clock),
  .coreid             (0),
  .valid              (trap),
  .code               (trap_code),
  .pc                 (cmt_pc),
  .cycleCnt           (cycleCnt),
  .instrCnt           (instrCnt)
);

DifftestArchEvent DifftestArchEvent(
  .clock              (clock),
  .coreid             (0),
  .intrNO             (intrNO),   //zhongduanhao
  .cause              (cause),   //liwaihao 
  .exceptionPC        (exceptionPC),
  .exceptionInst      (exceptionInst)
);


DifftestCSRState DifftestCSRState(
  .clock              (clock),
  .coreid             (0),
  .priviledgeMode     (`RISCV_PRIV_MODE_M),
  .mstatus            (csr_mstatus_diff),
  .sstatus            (csr_sstatus_diff),
  .mepc               (csr_mepc_diff),
  .sepc               (0),
  .mtval              (0),
  .stval              (0),
  .mtvec              (csr_mtvec_diff),
  .stvec              (0),
  .mcause             (csr_mcause_diff),
  .scause             (0),
  .satp               (0),
  //.mip                (csr_mip_diff),
  .mip                (0),
  .mie                (csr_mie_diff),
  .mscratch           (csr_mscratch_diff),
  .sscratch           (0),
  .mideleg            (0),
  .medeleg            (0)
);

DifftestArchFpRegState DifftestArchFpRegState(
  .clock              (clock),
  .coreid             (0),
  .fpr_0              (0),
  .fpr_1              (0),
  .fpr_2              (0),
  .fpr_3              (0),
  .fpr_4              (0),
  .fpr_5              (0),
  .fpr_6              (0),
  .fpr_7              (0),
  .fpr_8              (0),
  .fpr_9              (0),
  .fpr_10             (0),
  .fpr_11             (0),
  .fpr_12             (0),
  .fpr_13             (0),
  .fpr_14             (0),
  .fpr_15             (0),
  .fpr_16             (0),
  .fpr_17             (0),
  .fpr_18             (0),
  .fpr_19             (0),
  .fpr_20             (0),
  .fpr_21             (0),
  .fpr_22             (0),
  .fpr_23             (0),
  .fpr_24             (0),
  .fpr_25             (0),
  .fpr_26             (0),
  .fpr_27             (0),
  .fpr_28             (0),
  .fpr_29             (0),
  .fpr_30             (0),
  .fpr_31             (0)
);

endmodule
